''' <summary>
''' Class for all classes to read BMD data
''' </summary>
Public Class clsDataBMD
    Inherits clsData

    'local variable(s) to hold property value(s)
    'Private _BMD As clsBMD
    Private _TimeNum As Integer
    Private _SegmentNum As Integer 'keeps track of current segment number; sequential reads loop through all times, then all stations,

    ''' <summary>
    ''' Flag to set whether entire contents of BMD is loaded into RAM or kept on disk
    ''' </summary>
    Public Property InRam() As Boolean
        Get
            Return _BMD.InRam
        End Get
        Set(ByVal value As Boolean)
            _BMD.InRam = value
        End Set
    End Property

    Public Property BMD As clsBMD

    Public Overrides Sub Initialize(ByVal _Filename As String)
        MyBase.Initialize(_Filename)
        mDataType = enumDataType.BMD
        _BMD = New clsBMD(_Filename, False, , False)
        With _BMD
            .InRam = False
            .BypassBoundsChecking = True
            For Each s As String In .SegmentNames
                If Not mStationIDs.Contains(s) Then mStationIDs.Add(s)
            Next

            'treat bmd file as a tabular data file, where the station ID (segment) and date-time are in columns 0 and 1, and each variable is in subsequent columns
            mFieldNames.Clear()
            mFieldNames.Add("Station_ID")
            mFieldNames.Add("Date_Time")
            ReDim mMinValue(.NumVars + 2 - 1)
            ReDim mMaxValue(.NumVars + 2 - 1)
            For v As Integer = 0 To .NumVars - 1
                Dim PCode As String = .VarName(v).Replace(Chr(0), "").Trim
                Dim num As Integer = 0
                While mPCodes.Contains(PCode)
                    num += 1
                    PCode &= num
                End While
                mPCodes.Add(PCode)
                mFieldNames.Add(PCode & IIf(.VarUnits(v).Trim = "", "", " (" & .VarUnits(v).Replace(Chr(0), "").Trim & ")"))
                mMinValue(mFieldNames.Count - 1) = .VarMin(v)
                mMaxValue(mFieldNames.Count - 1) = .VarMax(v)
            Next
            mMinDate = _BMD.Time(0)
            mMaxDate = _BMD.Time(_BMD.NumTimes - 1)
            _SegmentNum = 0
        End With
    End Sub

    Public Overrides Property ActiveStationID() As String
        Get
            Return MyBase.ActiveStationID
        End Get
        Set(ByVal value As String)
            MyBase.ActiveStationID = value
            _SegmentNum = StationIDs.IndexOf(value)
        End Set
    End Property

    ''' <summary>
    ''' Read next line from file into memory, storing into mItems array
    ''' Also save mLastLineRead for error reporting
    ''' </summary>
    ''' <returns>True if successful, false if eof reached and no more data</returns>
    Public Overrides Function Read() As Boolean
        Try
            If Not MyBase.Read() Then Return False
            With _BMD
                If _TimeNum > .NumTimes - 1 Then 'go to next segment
                    If mActiveStationIDSet Then
                        Return False 'only read one stationid
                    Else
                        _SegmentNum += 1
                        _TimeNum = 0
                        If _SegmentNum > .NumSegments - 1 Then Return False
                    End If
                End If
                mItems.Clear()
                mItems.Add(.SegmentNames(_SegmentNum))
                mItems.Add(.Time(_TimeNum).ToString(MyDateTimeFormat))
                Dim ConcAtTime() As Single = .Conc(_SegmentNum, _TimeNum)

                For i As Integer = 0 To .NumVars - 1
                    'mItems.Add(.Conc(i, _SegmentNum, _TimeNum))
                    mItems.Add(ConcAtTime(i))
                Next
                _TimeNum += 1
                Return True
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
            Return False
        End Try
    End Function

    Public Overrides Sub Rewind()
        MyBase.Rewind()
        _TimeNum = 0
        _SegmentNum = 0
    End Sub

    ''' <summary>
    ''' Determine percent of file that has been read
    ''' </summary>
    ''' <returns>Percent of file read</returns>
    Public Overrides Function PercentComplete() As Double
        Return mRecordNum * 100.0 / (_BMD.NumTimes * _BMD.NumSegments)
    End Function

    ''' <summary>
    ''' Determine min/max values for each pcodes regardless of station id; generic version uses brute force by looping over all entries
    ''' </summary>
    ''' <remarks></remarks>
    Protected Overrides Sub GetMinMaxDates()
        mMinDate = _BMD.Time(0)
        mMaxDate = _BMD.Time(_BMD.NumTimes - 1)
    End Sub

    ''' <summary>
    ''' Get data table containing time series data for all stations and pcodes
    ''' Table will contain station_id, date-time, and all variable fields
    ''' </summary>
    Public Overloads Function GetDataTable() As System.Data.DataTable
        Dim dt As DataTable = MyBase.SetupDataTable(enumDataTableColumns.All)
        With _BMD
            For s As Integer = 0 To .NumSegments - 1
                For v As Integer = 0 To .NumVars - 1
                    For t As Integer = 0 To .NumTimes - 1
                        Dim r As DataRow = dt.NewRow
                        r(0) = .SegmentName(s)
                        r(1) = .Time(t).ToString(MyDateTimeFormat)
                        For j As Integer = 0 To .NumVars - 1
                            r.Item(j + 2) = .Conc(v, s, t)
                        Next
                        dt.Rows.Add(r)
                    Next
                Next
                RaiseProgress(s, .NumSegments - 1, "Loading data...")
            Next
        End With
        Return dt
    End Function

    ''' <summary>
    ''' Get data table containing time series data for a specified station id and pcode
    ''' Table will contain date-time field and result field
    ''' </summary>
    Public Overloads Function GetDataTable(ByVal StationID As String, ByVal PCode As String) As DataTable
        Dim staNum As Integer = -1
        If mStationIDs.Contains(StationID) Then
            staNum = mStationIDs.IndexOf(StationID)
        Else
            Throw New ApplicationException("BMD file does not contain the station: " & StationID)
            Return Nothing
        End If
        Dim itemNum As Integer = -1
        If ContainsFieldName(PCode) Then
            itemNum = mPCodes.IndexOf(PCode)
        Else
            Throw New ApplicationException("BMD file does not contain the column: " & PCode)
            Return Nothing
        End If
        Dim dt As DataTable = MyBase.GetDataTable(StationID, PCode)
        With _BMD
            For t As Integer = 0 To .NumTimes - 1
                Dim result As Single = .Conc(itemNum, staNum, t)
                Debug.Assert(Not Single.IsNaN(result))
                dt.Rows.Add(.Time(t), result)
                RaiseProgress(t, .NumTimes - 1, "Loading data...")
            Next
        End With
        Return dt
    End Function

    ''' <summary>
    ''' Get data table containing time series data for a all stations and pcode
    ''' Table will contain station field, date-time field and result field
    ''' </summary>
    Public Overloads Function GetDataTable(ByVal StationIDs() As String, ByVal PCode As String) As DataTable
        Dim StationNums(StationIDs.Length - 1) As Integer
        For i As Integer = 0 To StationIDs.Length - 1
            Dim staNum As Integer = -1
            If mStationIDs.Contains(StationIDs(i)) Then
                StationNums(i) = mStationIDs.IndexOf(StationIDs(i))
            Else
                Throw New ApplicationException("BMD file does not contain the station: " & StationIDs(i))
                Return Nothing
            End If
        Next
        Dim itemNum As Integer = -1
        If ContainsFieldName(PCode) Then
            itemNum = mPCodes.IndexOf(PCode)
        Else
            Throw New ApplicationException("BMD file does not contain the column: " & PCode)
            Return Nothing
        End If

        Dim dt As DataTable = MyBase.SetupDataTable(enumDataTableColumns.Station_Result)
        With _BMD
            For t As Integer = 0 To .NumTimes - 1
                Dim Concs(,) As Single = .Conc(t)
                Dim t1 As Date = .Time(t)
                For i As Integer = 0 To StationNums.Length - 1
                    Dim result As Single = Concs(StationNums(i), itemNum)
                    dt.Rows.Add(StationIDs(i), t1, result)
                Next
                RaiseProgress(t, .NumTimes - 1, "Loading data...")
                Erase Concs
            Next
        End With
        Erase StationNums
        Return dt
    End Function

    ''' <summary>
    ''' Get data table containing all data in given time range for all station ids and pcode
    ''' Table will contain station and result field
    ''' </summary>
    ''' <param name="StationIDList">List of Desired Station IDs</param>
    ''' <param name="PCode">Desired PCode</param>
    ''' <param name="MinDate">Minimum date to include</param>
    ''' <param name="MaxDate">Maximum date to include</param>
    Public Overrides Function GetResults(StationIDList As Generic.List(Of String),
                                         LinkageType As enumLinkageType,
                                         PCode As String,
                                         MinDate As Date,
                                         MaxDate As Date,
                                         AggregateFunction As enumAggregateFunction) As Generic.Dictionary(Of String, Single)

        'if parseIJ then create dictionary linking station IDs with IJ values
        If LinkageType = enumLinkageType.ParseIJK Then
            dictIJK_StationID = New Dictionary(Of String, String)
            For Each staID As String In mStationIDs
                Dim IJ As String = ""
                If TryParseIJK(staID, IJ) AndAlso Not dictIJK_StationID.ContainsKey(IJ) Then dictIJK_StationID.Add(IJ, staID)
            Next
        End If

        Dim itemNum As Integer = mPCodes.IndexOf(PCode)
        If itemNum = -1 Then Return Nothing

        If MaxDate < MinDate Then
            Throw New ApplicationException("BMD requested dates are invalid")
            Return Nothing
        End If

        Dim dictResults As New Generic.Dictionary(Of String, Single)

        With _BMD

            Dim tmin As Integer = .TimeIndex(MinDate)
            Dim tmax As Integer = Math.Max(tmin, .TimeIndex(MaxDate) - 1)

            If MinDate <= mMinDate And MaxDate >= mMaxDate And AggregateFunction <= enumAggregateFunction.Max Then
                'special case: asking for min/max value over entire period of record--get from bmd file
                For Each StaID As String In StationIDList
                    Dim SegNum As Integer
                    If LinkageType = enumLinkageType.SegmentNum Then
                        SegNum = Val(StaID) - 1
                    ElseIf LinkageType = enumLinkageType.ParseIJK Then 'StaID contains desired I,J pair
                        Dim stationID As String = ""
                        If dictIJK_StationID.TryGetValue(StaID, stationID) Then
                            SegNum = .SegmentIndex(stationID)
                        Else
                            SegNum = -1
                        End If
                    Else
                        SegNum = .SegmentIndex(StaID)
                    End If
                    Dim result As Single = -1
                    If SegNum <> -1 AndAlso SegNum >= 0 AndAlso SegNum <= .NumSegments - 1 Then
                        Select Case AggregateFunction
                            Case enumAggregateFunction.Max : result = .VarSegMax(itemNum, SegNum)
                            Case enumAggregateFunction.Min : result = .VarSegMin(itemNum, SegNum)
                            Case enumAggregateFunction.Num : result = .NumTimes
                        End Select
                    End If
                    dictResults.Add(StaID, result)
                Next
            ElseIf AggregateFunction = enumAggregateFunction.Num Then
                'just return number of time steps in period
                For Each StaID As String In StationIDList
                    dictResults.Add(StaID, tmax - tmin)
                Next
            Else
                'must loop over date range and calculate values
                'For t As Integer = tmin To tmax
                '    Dim Concs(,) As Single = .Conc(t, t = tmin)
                '    Dim sum As Single = 0
                '    Dim min As Single = Single.MaxValue
                '    Dim max As Single = Single.MinValue

                '    For Each StaID As String In StationIDList
                '        If t = tmin Then 'initialize values
                '            Select Case AggregateFunction
                '                Case enumAggregateFunction.Min : dictResults.Add(StaID, Single.MaxValue)
                '                Case enumAggregateFunction.Max : dictResults.Add(StaID, Single.MinValue)
                '                Case enumAggregateFunction.Mean : dictResults.Add(StaID, 0)
                '            End Select
                '        End If
                '        Dim SegNum As Integer
                '        If LinkageType = enumLinkageType.SegmentNum Then
                '            SegNum = StaID - 1
                '        ElseIf LinkageType = enumLinkageType.ParseIJK Then
                '            Dim stationID As String = ""
                '            If dictIJK_StationID.TryGetValue(StaID, stationID) Then
                '                SegNum = .SegmentIndex(stationID)
                '            Else
                '                SegNum = -1
                '            End If
                '        Else
                '            SegNum = .SegmentIndex(StaID) 'note: segID is 1-based, but index is 0 based
                '        End If
                '        If SegNum <> -1 AndAlso SegNum >= 0 AndAlso SegNum <= .NumSegments - 1 Then
                '            Dim conc As Single = Concs(SegNum, itemNum)
                '            Select Case AggregateFunction
                '                Case enumAggregateFunction.Min : dictResults(StaID) = Math.Min(dictResults(StaID), conc)
                '                Case enumAggregateFunction.Max : dictResults(StaID) = Math.Max(dictResults(StaID), conc)
                '                Case enumAggregateFunction.Mean : dictResults(StaID) += conc
                '            End Select
                '        End If
                '    Next
                'Next
                ''reset any missing values to -1
                'For Each StaID As String In StationIDList
                '    If dictResults.ContainsKey(StaID) Then
                '        If LinkageType = enumLinkageType.SegmentNum AndAlso StaID = -1 Then
                '            dictResults(StaID) = -1
                '        ElseIf LinkageType = enumLinkageType.ParseIJK AndAlso Not dictIJK_StationID.ContainsKey(StaID) Then
                '            dictResults(StaID) = -1
                '        ElseIf LinkageType = enumLinkageType.StationID AndAlso .SegmentIndex(StaID) = -1 Then
                '            dictResults(StaID) = -1
                '        ElseIf AggregateFunction = enumAggregateFunction.Mean Then
                '            dictResults(StaID) /= (tmax - tmin + 1)
                '        End If
                '    End If
                'Next

                'must loop over date range and calculate values

                Dim dictResultList As New Dictionary(Of String, List(Of Double)) 'will contain time series of values

                For t As Integer = tmin To tmax
                    Dim Concs(,) As Single = .Conc(t, t = tmin)

                    For Each StaID As String In StationIDList

                        If t = tmin Then 'initialize list
                            dictResultList.Add(StaID, New List(Of Double))
                        End If

                        Dim SegNum As Integer
                        If LinkageType = enumLinkageType.SegmentNum Then
                            SegNum = StaID - 1
                        ElseIf LinkageType = enumLinkageType.ParseIJK Then
                            Dim stationID As String = ""
                            If dictIJK_StationID.TryGetValue(StaID, stationID) Then
                                SegNum = .SegmentIndex(stationID)
                            Else
                                SegNum = -1
                            End If
                        Else
                            SegNum = .SegmentIndex(StaID) 'note: segID is 1-based, but index is 0 based
                        End If

                        If SegNum <> -1 AndAlso SegNum >= 0 AndAlso SegNum <= .NumSegments - 1 Then
                            Dim conc As Single = Concs(SegNum, itemNum)
                            dictResultList(StaID).Add(conc)
                        End If
                    Next
                Next
                'reset any missing values to -1
                For Each StaID As String In dictResultList.Keys
                    If LinkageType = enumLinkageType.SegmentNum AndAlso StaID = -1 Then
                        dictResults(StaID) = -1
                    ElseIf LinkageType = enumLinkageType.ParseIJK AndAlso Not dictIJK_StationID.ContainsKey(StaID) Then
                        dictResults(StaID) = -1
                    ElseIf LinkageType = enumLinkageType.StationID AndAlso .SegmentIndex(StaID) = -1 Then
                        dictResults(StaID) = -1
                    Else
                        Dim agg As Double = -1
                        Dim sum As Double = 0.0, min As Double = Double.MaxValue, max As Double = Double.MinValue
                        Dim sumlog As Double = 0.0, numlog As Integer = 0
                        Dim arPct(18) As Double

                        Select Case AggregateFunction
                            Case enumAggregateFunction.Min, enumAggregateFunction.Max, enumAggregateFunction.Mean
                                For Each d As Double In dictResultList(StaID)
                                    sum += d
                                    min = Math.Min(min, d)
                                    max = Math.Max(max, d)
                                Next
                            Case enumAggregateFunction.Geomean
                                For Each d As Double In dictResultList(StaID)
                                    If d > 0.0 Then
                                        numlog += 1
                                        sumlog += Math.Log(d)
                                    End If
                                Next
                            Case enumAggregateFunction.Num 'will calc below
                            Case Else
                                modProbability.CalcStats(dictResultList(StaID).ToArray, arPct)
                        End Select
                        Select Case AggregateFunction
                            Case enumAggregateFunction.Num : agg = dictResultList(StaID).Count
                            Case enumAggregateFunction.Min : agg = min
                            Case enumAggregateFunction.Max : agg = max
                            Case enumAggregateFunction.Mean : If dictResultList(StaID).Count > 0 Then agg = sum / dictResultList(StaID).Count
                            Case enumAggregateFunction.Geomean : If numlog > 0 Then agg = Math.Exp(sumlog / numlog)
                            Case enumAggregateFunction.Median : agg = arPct(9)
                            Case enumAggregateFunction.Pct5 : agg = arPct(0)
                            Case enumAggregateFunction.Pct10 : agg = arPct(1)
                            Case enumAggregateFunction.Pct25 : agg = arPct(4)
                            Case enumAggregateFunction.Pct75 : agg = arPct(14)
                            Case enumAggregateFunction.Pct90 : agg = arPct(17)
                            Case enumAggregateFunction.Pct95 : agg = arPct(18)
                        End Select
                        dictResults(StaID) = agg
                    End If
                Next
            End If
        End With
        Return dictResults
    End Function

    ''' <summary>
    ''' Get aggregate value for all data in given time range for station id and pcode
    ''' If requested station ID or PCode is not found, will throw exception and return double.minvalue
    ''' This routine is called by frmGIS and is used for animation: all values at a given time or range are assembled for rapid display
    ''' Has been overridden for BMD, and can also override for other types to make them faster (TO-DO item)
    ''' This version will return magnitude and direction tuple for display of vector data
    ''' </summary>
    ''' <param name="StationIDList">List of desired Station ID</param>
    ''' <param name="PCodeX">Desired PCode for x component</param>
    ''' <param name="PCodeY">Desired PCode for y component</param>
    ''' <param name="MinDate">Minimum date to include (inclusive)</param>
    ''' <param name="MaxDate">Maximum date to include (non-inclusive)</param>
    ''' <param name="AggregateFunction">How the raw data must be aggregated (note: only some functions are supported; caller will really only need means for velocity vectors)</param>
    Public Overrides Function GetResults(StationIDList As Generic.List(Of String),
                                         PCodeX As String, PCodeY As String,
                                         MinDate As Date, MaxDate As Date,
                                         AggregateFunction As enumAggregateFunction) As Generic.Dictionary(Of String, Tuple(Of Single, Single))

        Dim itemNumX As Integer = mPCodes.IndexOf(PCodeX)
        Dim itemNumY As Integer = mPCodes.IndexOf(PCodeY)

        If itemNumX = -1 Or itemNumY = -1 Then
            Throw New ApplicationException("PCodes were not recognized: " & PCodeX & ", " & PCodeY)
            Return Nothing
        End If

        If MaxDate < MinDate Then
            Throw New ApplicationException("BMD requested dates are invalid")
            Return Nothing
        End If

        Dim dictResults As New Generic.Dictionary(Of String, Tuple(Of Single, Single))

        With _BMD

            'if all items in the station id list are numeric then assume is list of segment numbers rather than station IDs
            Dim IsSegNum As Boolean = True
            For Each s As String In StationIDList
                If Not IsNumeric(s) Then
                    IsSegNum = False
                    Exit For
                End If
            Next

            Dim tmin As Integer = .TimeIndex(MinDate)
            Dim tmax As Integer = Math.Max(tmin, .TimeIndex(MaxDate) - 1)

            If MinDate <= mMinDate And MaxDate >= mMaxDate And AggregateFunction <> enumAggregateFunction.Mean Then
                'special case: asking for min/max value over entire period of record--get from bmd file
                For Each StaID As String In StationIDList
                    Dim SegNum As Integer
                    If IsSegNum Then
                        SegNum = Val(StaID) - 1
                    Else
                        SegNum = .SegmentIndex(StaID)
                    End If
                    Dim l As Single = -1, a As Single = -1
                    If SegNum <> -1 AndAlso SegNum >= 0 AndAlso SegNum <= .NumSegments - 1 Then
                        Select Case AggregateFunction
                            Case enumAggregateFunction.Max
                                l = Math.Sqrt(.VarSegMax(itemNumX, SegNum) ^ 2 + .VarSegMax(itemNumY, SegNum) ^ 2)
                                a = Math.Atan2(.VarSegMax(itemNumY, SegNum), .VarSegMax(itemNumX, SegNum))
                            Case enumAggregateFunction.Min
                                l = Math.Sqrt(.VarSegMin(itemNumX, SegNum) ^ 2 + .VarSegMin(itemNumY, SegNum) ^ 2)
                                a = Math.Atan2(.VarSegMin(itemNumY, SegNum), .VarSegMin(itemNumX, SegNum))
                            Case enumAggregateFunction.Num
                                l = .NumTimes
                                a = 0
                        End Select
                    End If
                    dictResults.Add(StaID, New Tuple(Of Single, Single)(l, a))
                Next
            ElseIf AggregateFunction = enumAggregateFunction.Num Then
                'just return number of time steps in period
                For Each StaID As String In StationIDList
                    dictResults.Add(StaID, New Tuple(Of Single, Single)(tmax - tmin, 0))
                Next
            Else
                'must loop over date range and calculate values
                For t As Integer = tmin To tmax
                    Dim Concs(,) As Single = .Conc(t, t = tmin)

                    For Each StaID As String In StationIDList
                        If t = tmin Then 'initialize values
                            Select Case AggregateFunction
                                Case enumAggregateFunction.Min : dictResults.Add(StaID, New Tuple(Of Single, Single)(Single.MaxValue, 0)) 'items are min length & associated angle
                                Case enumAggregateFunction.Max : dictResults.Add(StaID, New Tuple(Of Single, Single)(Single.MinValue, 0)) 'items are max length & associated angle
                                Case enumAggregateFunction.Mean : dictResults.Add(StaID, New Tuple(Of Single, Single)(0, 0)) 'items are sum xlength & sum ylength
                            End Select
                        End If
                        Dim SegNum As Integer
                        If IsSegNum Then SegNum = StaID - 1 Else SegNum = .SegmentIndex(StaID) 'note: segID is 1-based, but index is 0 based
                        If SegNum <> -1 AndAlso SegNum >= 0 AndAlso SegNum <= .NumSegments - 1 Then
                            Dim x As Single = Concs(SegNum, itemNumX)
                            Dim y As Single = Concs(SegNum, itemNumY)
                            Dim l As Single = Math.Sqrt(x * x + y * y)
                            Dim a As Single = Math.Atan2(y, x)
                            Select Case AggregateFunction
                                Case enumAggregateFunction.Min
                                    If l < dictResults(StaID).Item1 Then dictResults(StaID) = New Tuple(Of Single, Single)(l, a)
                                Case enumAggregateFunction.Max
                                    If l > dictResults(StaID).Item1 Then dictResults(StaID) = New Tuple(Of Single, Single)(l, a)
                                Case enumAggregateFunction.Mean
                                    dictResults(StaID) = New Tuple(Of Single, Single)(dictResults(StaID).Item1 + x, dictResults(StaID).Item2 + y)
                            End Select
                        End If
                    Next
                Next
                'reset any missing values to -1 and calculate mean
                For Each StaID As String In StationIDList
                    If dictResults.ContainsKey(StaID) Then
                        If IsSegNum And StaID = -1 Then
                            dictResults(StaID) = New Tuple(Of Single, Single)(-1, -1)
                        ElseIf Not IsSegNum AndAlso .SegmentIndex(StaID) = -1 Then
                            dictResults(StaID) = New Tuple(Of Single, Single)(-1, -1)
                        ElseIf AggregateFunction = enumAggregateFunction.Mean Then
                            Dim x As Single = dictResults(StaID).Item1
                            Dim y As Single = dictResults(StaID).Item2
                            Dim l As Single = Math.Sqrt(x * x + y * y) / (tmax - tmin + 1)
                            Dim a As Single = Math.Atan2(y, x)
                            dictResults(StaID) = New Tuple(Of Single, Single)(l, a)
                        End If
                    End If
                Next
            End If
        End With
        Return dictResults

    End Function

    ''' <summary>
    ''' Retrieve all dates in BMD 
    ''' </summary>
    Public Overrides Function Dates() As List(Of DateTime)
        Dim lst As New List(Of DateTime)
        lst.AddRange(BMD.Times)
        Return lst
    End Function

    Public Overrides Sub Close()
        MyBase.Close()
        _BMD.Close()
    End Sub
End Class

